#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <omp.h>
#include <stdbool.h>

#define UNROLL
#define REAL double
#define THRESHOLD 1e-6
#define SWAP(_T, a, b) \
    {                  \
        _T tmp = a;    \
        a = b;         \
        b = tmp;       \
    }
#define MIN(a, b) ((a)<(b) ? (a) : (b))

inline static void
swap_data(REAL *a, REAL *b)
{
    REAL tmp = *a;
    *a = *b;
    *b = tmp;
}

inline static void
swap_index(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

enum MatType
{
    PRIMARY,
    ROTATE,
    OUTPUT
};

inline static REAL
sign(REAL n)
{
    return n > 0 ? 1.0 : -1.0;
}

// data format should be column-major
struct matrix_t
{
    int h;
    int w;
    REAL *in;
    REAL *out;
    REAL *norms;
    int *col_idx;
};

// basic matrix ops
inline static void init_matrix(struct matrix_t *mat, int height, int width, enum MatType type)
{
    mat->h = height;
    mat->w = width;
    mat->in = (REAL *)malloc(sizeof(REAL) * height * width);
    if (type <= ROTATE)
    {
        mat->out = (REAL *)malloc(sizeof(REAL) * height * width);
        if (type == PRIMARY)
        {
            mat->col_idx = (int *)malloc(sizeof(int) * mat->w);
            mat->norms = (REAL *)malloc(sizeof(REAL) * mat->w);
        }
    }
    else
    {
        mat->out = mat->in;
    }
    memset(mat->in, 0, sizeof(REAL) * height * width);
    memset(mat->out, 0, sizeof(REAL) * height * width);
}

void update_norm(struct matrix_t);
void sort_norm(struct matrix_t);
bool grouped_rotate(struct matrix_t, struct matrix_t);
int format_output(struct matrix_t, struct matrix_t, REAL*);
void normalize(struct matrix_t, struct matrix_t, REAL *, int);

// IO functions
void matrix_from_file(struct matrix_t *, const char *file);
void matrix_to_file(struct matrix_t, const char *file);
void vector_to_file(REAL *, int, const char *);

// Ring Seq pairings
struct colpair_t
{
    int *colidx_a;
    int *colidx_b;
    int len;
    int iters;
};
void init_pair(int);
void reset_pair();
#ifndef UNROLL
void switch_pair(int);
#else
void switch_pair(int,int);
#endif